
// WorldBuilder.cpp : Defines the class behaviors for the application.
//

#include "stdafx.h"
#include "afxwinappex.h"
#include "WorldBuilder.h"
#include "MainFrm.h"
#include "WorldBuilderDoc.h"
#include "MainView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CWorldBuilderApp

BEGIN_MESSAGE_MAP(CWorldBuilderApp, CWinAppEx)
    ON_COMMAND(ID_APP_ABOUT, &CWorldBuilderApp::OnAppAbout)
    // Standard file based document commands
    ON_COMMAND(ID_FILE_NEW, &CWinAppEx::OnFileNew)
    ON_COMMAND(ID_FILE_OPEN, &CWinAppEx::OnFileOpen)
END_MESSAGE_MAP()


// CWorldBuilderApp construction

CWorldBuilderApp::CWorldBuilderApp()
: m_lastTickReady(false), m_ui(&m_world)
{
    LARGE_INTEGER ticksPerSecond;
    QueryPerformanceFrequency(&ticksPerSecond);
    m_FreqMicroSecs = static_cast<double>(ticksPerSecond.QuadPart) / (1000. * 1000.);

    m_bHiColorIcons = TRUE;

    // TODO: add construction code here,
    // Place all significant initialization in InitInstance
}

CWorldBuilderApp::~CWorldBuilderApp()
{
}

// The one and only CWorldBuilderApp object

CWorldBuilderApp theApp;
View3d* theView;


// CWorldBuilderApp initialization

BOOL CWorldBuilderApp::InitInstance()
{
    CWinAppEx::InitInstance();

    // TODO put this somewhere else
    //Load the scintilla dll
    m_hSciDLL = ::LoadLibrary(_T("SciLexer.dll"));
    if(m_hSciDLL == NULL)
    { 
        AfxMessageBox(_T("Scintilla DLL is not installed, please \
            download the SciTE editor and copy the SciLexer.dll \
            into this application's directory"));
        return FALSE;
    }

    // OLE must be initialize to use the clipboard functionality
    if(AfxOleInit() == FALSE)
    {
        AfxMessageBox(_T("Couldn't initialize OLE"));
        return FALSE;
    }

    // Standard initialization
    // If you are not using these features and wish to reduce the size
    // of your final executable, you should remove from the following
    // the specific initialization routines you do not need
    // Change the registry key under which our settings are stored
    // TODO: You should modify this string to be something appropriate
    // such as the name of your company or organization
    
    SetRegistryKey(_T("Local AppWizard-Generated Applications"));
    
    LoadStdProfileSettings(4);  // Load standard INI file options (including MRU)

    InitContextMenuManager();

    InitKeyboardManager();

    InitTooltipManager();
    CMFCToolTipInfo ttParams;
    ttParams.m_bVislManagerTheme = TRUE;
    theApp.GetTooltipManager()->SetTooltipParams(AFX_TOOLTIP_TYPE_ALL,
        RUNTIME_CLASS(CMFCToolTipCtrl), &ttParams);

    //See 
    //  http://msdn.microsoft.com/en-us/library/a8x03wk3.aspx
    //  http://msdn.microsoft.com/en-us/library/hts9a4xz.aspx
    //  http://msdn.microsoft.com/en-us/library/7yha6tek.aspx
    //  http://msdn.microsoft.com/en-us/library/d1e9fe7d.aspx
    //  http://msdn.microsoft.com/en-us/library/2b4xctyw.aspx

    // Register the application's document templates.  Document templates
    //  serve as the connection between documents, frame windows and views
    CSingleDocTemplate* pDocTemplate;
    pDocTemplate = new CSingleDocTemplate(
        IDR_MAINFRAME,
        RUNTIME_CLASS(CWorldBuilderDoc),
        RUNTIME_CLASS(CMainFrame),       // main SDI frame window
        RUNTIME_CLASS(CMainView));
    if (!pDocTemplate)
        return FALSE;
    AddDocTemplate(pDocTemplate);

    // Parse command line for standard shell commands, DDE, file open
    CCommandLineInfo cmdInfo;
    ParseCommandLine(cmdInfo);

    // Dispatch commands specified on the command line.  Will return FALSE if
    // app was launched with /RegServer, /Register, /Unregserver or /Unregister.
    if (!ProcessShellCommand(cmdInfo))
        return FALSE;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    // At this point we know that all the "views" have been built, so let's
    // register them as world views. 
    
    theView = getMainView();
    m_ui.addWorldFollower(getPropertiesWnd());
    m_ui.addWorldFollower(getSceneTreeCtrl());
    m_ui.addWorldFollower(getActiveView());

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    // Now associate them to the UI as well

    m_ui.addUiFollower(getActiveView());
    m_ui.addUiFollower(getSceneTreeCtrl());
    m_ui.addUiFollower(getPropertiesWnd());
    m_ui.addUiFollower(getOutputWnd());

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    // NOTE the previous way of connecting the different components is not 
    // very  elegant but it's the only way considering that we can't pass
    // arguments to the MainFrame constructor, and that the MainFrame is
    // who creates all of these windows. Ideally we would pass the WOrld
    // and UI as arguments to the CMainFrame constructor, which in turn would
    // forward them to the constructors of the different subwindows, which
    // would register themselves as World and UI viewers.

    m_ui.getSelectionManager().clearSelection(true);

    // Add the node manager as a drawable to the view
    // TODO this is far from elegant or flexible
    //getActiveView()->addDrawable(&NodeManager::instance());
    //getActiveView()->addPickManager(&NodeManager::instance());

    // Add the shader manager as a listener of main view render events
    getMainView()->addRenderListener(&m_world.getShaderManager());

    // The one and only window has been initialized, so show and update it
    m_pMainWnd->ShowWindow(SW_SHOW);
    m_pMainWnd->UpdateWindow();
    // call DragAcceptFiles only if there's a suffix
    //  In an SDI app, this should occur after ProcessShellCommand

    return TRUE;
}



// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
    CAboutDlg();

// Dialog Data
    enum { IDD = IDD_ABOUTBOX };

protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

// Implementation
protected:
    DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()

// App command to run the dialog
void CWorldBuilderApp::OnAppAbout()
{
    CAboutDlg aboutDlg;
    aboutDlg.DoModal();
}

// CWorldBuilderApp customization load/save methods

void CWorldBuilderApp::PreLoadState()
{
    BOOL bNameValid;
    CString strName;
    bNameValid = strName.LoadString(IDS_EDIT_MENU);
    ASSERT(bNameValid);
    GetContextMenuManager()->AddMenu(strName, IDR_POPUP_EDIT);
    bNameValid = strName.LoadString(IDS_EXPLORER);
    ASSERT(bNameValid);
    GetContextMenuManager()->AddMenu(strName, IDR_POPUP_EXPLORER);
}

void CWorldBuilderApp::LoadCustomState()
{
}

void CWorldBuilderApp::SaveCustomState()
{
}

// CWorldBuilderApp message handlers

CMainFrame* CWorldBuilderApp::getMainFrame() const
{
    return (CMainFrame*)m_pMainWnd;
}

CMainView * CWorldBuilderApp::getActiveView() const
{
    return (CMainView*)(getMainFrame()->GetActiveView());
}

CMainView* CWorldBuilderApp::getMainView() const
{
    CMainFrame* mainFrm = getMainFrame();
    ASSERT(mainFrm);
    return (CMainView*)(mainFrm->GetDlgItem(AFX_IDW_PANE_FIRST));
}


CWorldBuilderDoc * CWorldBuilderApp::getActiveDocument() const
{
    return (CWorldBuilderDoc*)getMainFrame()->GetActiveDocument();
}

CPropertiesWnd * CWorldBuilderApp::getPropertiesWnd() const
{
    return (CPropertiesWnd *)getMainFrame()->GetDlgItem(ID_VIEW_PROPERTIESWND);
}

// TODO for some reason getMainFrame()->GetDlgItem returns NULL for
// ID_VIEW_FILEVIEW, which is why we have to resort to this horrible hack
std::map<int, CWnd *> __wnds__;

BOOL CALLBACK findChildWindow(HWND hwnd, LPARAM lParam)
{
    int desiredId = (int)lParam;
    CWnd * w = CWnd::FromHandle(hwnd);
    int id = w->GetDlgCtrlID();
    if(id == desiredId)
    {
        __wnds__[desiredId]	= w;
        return FALSE;
    }
    return TRUE;
}

CSceneTreeCtrl * CWorldBuilderApp::getSceneTreeCtrl() const
{
    if(__wnds__[ID_VIEW_FILEVIEW] == NULL)
    {
        ::EnumChildWindows(*getMainFrame(), &findChildWindow, LPARAM(ID_VIEW_FILEVIEW));
        ASSERT(__wnds__[ID_VIEW_FILEVIEW]);
    }
    return ((SceneTreeView *)__wnds__[ID_VIEW_FILEVIEW])->getSceneTreeCtrl();
}

COutputWnd * CWorldBuilderApp::getOutputWnd() const
{
    return (COutputWnd *)getMainFrame()->GetDlgItem(ID_VIEW_OUTPUTWND);
}


// In the future we might like to run an animation continuously. This is probably
// the easiest way to do it: http://www.gamedev.net/reference/articles/article2204.asp

double CWorldBuilderApp::getTimeMicro() const
{
   // Computer ticks elapsed since system started
    LARGE_INTEGER ticks;
    QueryPerformanceCounter(&ticks);
    
    // Convert to microseconds
    double elapsedMicro = double(ticks.QuadPart) / m_FreqMicroSecs;
    
    // Done
    return elapsedMicro; 
}

// In the future we might like to run an animation continuously. This is probably
// the easiest way to do it: http://www.gamedev.net/reference/articles/article2204.asp

BOOL CWorldBuilderApp::OnIdle(LONG lCount)
{
    // Let the base class CWinApp::OnIdle complete its processing 
    // before we attempt any additional idle loop processing.
    if(CWinApp::OnIdle(lCount))
    {
        return TRUE;  
    }

    m_ui.getActionManager().performNext();
    

   // Computer ticks elapsed since system started
    LARGE_INTEGER ticks;
    QueryPerformanceCounter(&ticks);
    
    // If this is the first time here, we'll have to pass a zero-time update
    if(!m_lastTickReady) 
    {
        m_lastCounter = ticks.QuadPart;
        m_lastTickReady = true;
    }

    // Computer timer delta since last time we checked, and convert it to microsecs
    double elapsedMicro = double(ticks.QuadPart - m_lastCounter) / m_FreqMicroSecs;
    
    // Notify the active view (right now we use it mainly to handle pressed keys)
    getActiveView()->onIdle(elapsedMicro);
    
    // Update this so that it's up to date next time this method is executed
    m_lastCounter = ticks.QuadPart;

    return TRUE; // Means we'll want to get more time as soon as possible
}


std::string readCompleteTextArchive(CArchive & ar)
{
    std::string result;
    CString unicodeStr;
    while(ar.ReadString(unicodeStr))
    {
        result.append(CT2CA(unicodeStr));
        result.append(" ");
    }
    return result; 
}

void writeTextToArchive(const std::string & str, CArchive & ar)
{
    // Use CString ctor. to perform conversion from char to wchar
    ar.WriteString(CString(str.c_str()));
}
int CWorldBuilderApp::ExitInstance()
{
    if (m_hSciDLL)
    {
        ::FreeLibrary(m_hSciDLL);
    }
    return CWinAppEx::ExitInstance();
}
